// automatic generation from syntax definition AL 

header {
package com.cea.papyrus.classdiagram.parsers.antlr;

import org.eclipse.uml2.uml.*;
import com.cea.papyrus.umlutils.Parameter;

import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Collection;
import java.util.Vector;

import com.cea.papyrus.classdiagram.parsers.texteditor.parameterlabel.IContext;
import com.cea.papyrus.classdiagram.parsers.util.Assert;
}

class ParameterLabelParser extends Parser;

options {
	k = 2;
	buildAST = false;
	defaultErrorHandler = false;
}


{
	public ParameterLabelParser(TokenStream lexer, com.cea.papyrus.umlutils.Parameter parameter) {
		this(lexer);
		this.parameter = parameter.getUml2Parameter();	
		this.contextPackage = new com.cea.papyrus.umlutils.Package(this.parameter.getNearestPackage());
		this.modifiersUsed = new HashMap<String, Boolean>();
		modifiersUsed.put("ordered", false);	
		modifiersUsed.put("unique", false);	
		modifiersUsed.put("nonunique", false);
	}
	
	/**
	 * Validation state (true = validation : does not modify the parameter
	 */
	private boolean isValidation = false;
	
	/**
	 * @return the isValidation boolean
	 */
	public boolean isValidation() {
		return isValidation;
	}

	/**
	 * @param isValidation the isValidation to set (true -> do not modify the parameter)
	 */
	public void setValidation(boolean isValidation) {
		this.isValidation = isValidation;
	}
	
	/**
	 * Modified Parameter 
	 */
	private org.eclipse.uml2.uml.Parameter parameter;
	
	/**
	 * @return the parameter
	 */
	public org.eclipse.uml2.uml.Parameter getParameter() {
		return parameter;
	}

	/**
	 * @param parameter the parameter to set
	 */
	public void setParameter(org.eclipse.uml2.uml.Parameter parameter) {
		this.parameter = parameter;
	}
    	
    /** debug mode */
    protected final boolean debugMode = false;  	
    	
    /**
     * Debug method (~System.out.println)
     */
    private void debug(Object debug) {
    	if(debugMode) {
    		System.out.println(debug);	
    	}	
    }
    
    /**
     * Error method (~System.err.println)
     */
    private void error(Object debug) {
    	if(debugMode) {
    		System.err.println(debug);	
    	}	
    }
    
    /**
     * Context (for completion processor)
     */
    private int context = IContext.DEFAULT;
    
    /**
	 * @return the context
	 */
	public int getContext() {
		return context;
	}

	/**
	 * @param context the context to set
	 */
	public void setContext(int context) {
		this.context = context;
	}
    
    // all possible modified elements on parameter (with default values : necessary ?!)
  	private String name;
    private ParameterDirectionKind direction;
   	private Type type;
   	private String defaultValue = null;
   	private int lowerMultiplicity = 1;	// default multiplicity = [1..1]
    private int upperMultiplicity = 1;	// default multiplicity = [1..1]
	private boolean isUnique = true;	// default value = true
  	private boolean isOrdered = false;	// default value = false     

    private com.cea.papyrus.umlutils.Package contextPackage = null;
    
	/**
	 * Apply values to the parameter. <BR> 
	 * Modifications can not be applied directly. The parser must wait the end, 
	 * until it reaches the end of the label. If the end is reached without any error,
	 * new values can be applied to the target parameter.
	 */
	private void applyValues() {
		if(!isValidation){
			debug("\nvalues applied :");
			debug("+  name: "+name);
			debug("+  direction: "+direction);
			debug("+  type: "+type);
			debug("+  lower Multiplicity: "+lowerMultiplicity);
			debug("+  upper Multiplicity: "+upperMultiplicity);
			debug("+  default Value: "+defaultValue);
			debug("+  Property modifiers");
			debug("+    isUnique: "+isUnique);
			debug("+    isOrdered: "+isOrdered);
			
			parameter.setName(name);
			parameter.setDirection(direction);
			parameter.setType(type);
			parameter.setLower(lowerMultiplicity);
			parameter.setUpper(upperMultiplicity);
			parameter.setDefault(defaultValue);
			parameter.setIsUnique(isUnique);
			parameter.setIsOrdered(isOrdered);
		} 
	}
	
	
	// vector that keeps modifiers already used (used for completion)
	public Map<String, Boolean> modifiersUsed; 
	
	/**
	 * @return the modifiersUsed
	 */
	public Map<String, Boolean> getModifiersUsed() {
		return modifiersUsed;
	}

	/**
	 * @param modifiersUsed the modifiersUsed to set
	 */
	public void setModifiersUsed(Map<String, Boolean> modifiersUsed) {
		this.modifiersUsed = modifiersUsed;
	}
}

//////////////////////////////////////////////////////////////////////////////////////////
// Rules 
//////////////////////////////////////////////////////////////////////////////////////////
label 
	:
	(WS)*
	(direction {context = IContext.DIRECTION; } (WS)*)?
	parameter_name { context = IContext.NAME; }
	(	((WS)+ COLON)
	| 	COLON
	) 
	{ context = IContext.AFTER_COLON; } 
	parameter_type { context = IContext.PARAMETER_TYPE; } (WS)*  
	(multiplicity { context = IContext.MULTIPLICITY; } (WS)* )?
	(default_value { context = IContext.DEFAULT_VALUE; })? 
	(parameter_modifiers { context = IContext.PARAMETER_MODIFIERS; })?
	{
		applyValues();
	}
	;
	
direction
	:
	(	"in"		{ direction = ParameterDirectionKind.IN_LITERAL; }
	|	"out"		{ direction = ParameterDirectionKind.OUT_LITERAL; }
	|	"inout"		{ direction = ParameterDirectionKind.INOUT_LITERAL; }
	|	"return" 	{ direction = ParameterDirectionKind.RETURN_LITERAL; }
	)
	;

parameter_name
	:
	id:IDENTIFIER	
	{ 
		name = id.getText();
	}
	;
	
parameter_type
	:
	(WS)*
	(	type
	|	LT "Undefined" GT)
	;
	
type
	:
	id:IDENTIFIER
	{
		String typeName = id.getText();
		com.cea.papyrus.umlutils.Type utilType = contextPackage.findTypeByName(typeName);
		if(utilType != null) {
			type = utilType.getUml2Type();
		} else {
			throw new RecognitionException("Type "+typeName+" not found for parameter "+parameter.getName());
		}
	}
	;
	
multiplicity
	:
	LSQUARE
	{ context = IContext.IN_MULTIPLICITY; }
	(
		lower_multiplicity POINT_POINT upper_multiplicity {
			if(lowerMultiplicity > upperMultiplicity && upperMultiplicity != -1) {
				throw new RecognitionException("Lower bound ("+lowerMultiplicity+") is greater than upper bound ("+upperMultiplicity+")");
			}
		}
	|	upper_multiplicity {
			if(upperMultiplicity == -1) {
				lowerMultiplicity = 0;
			} else {
				lowerMultiplicity = upperMultiplicity;
			}
		}
	)
	RSQUARE
	;
	
lower_multiplicity
	:
	in:INTEGER
	{
		lowerMultiplicity = Integer.parseInt(in.getText());
	}
	;

upper_multiplicity
	:
	upperMultiplicity=unlimited_natural
	;
	
unlimited_natural returns [int value = 0 ]
	:
	(	STAR			{ value = -1; }
	|	in:INTEGER		{ value = Integer.parseInt(in.getText()); }
	)
	;
	
default_value
	:
	EQ defaultValue = expression
	{
		// remove spaces at the begining 
		defaultValue = defaultValue.trim();
	}
	;
	
expression returns [String value  = ""]
	{
		StringBuffer buffer = new StringBuffer();
	}
	:	
	(
        options {
      	 	greedy=false;
	    }
		:   id:~(LCURLY)
		{
			buffer.append(id.getText());
		}
	)+
	{
		value = buffer.toString();
	}
	;
	
parameter_modifiers
	:
	LCURLY 
	{ context = IContext.PARAMETER_MODIFIER; }
	parameter_modifier
	( 
		{ context = IContext.PARAMETER_MODIFIER; }
		COMMA parameter_modifier 
	)*
	RCURLY
	;
	
parameter_modifier 
	:
	(WS)*
	(
	|	"ordered"	{ isOrdered = true; modifiersUsed.put("ordered", true);	}
	|	"unique"	{ isUnique = true; modifiersUsed.put("unique", true); modifiersUsed.put("nonunique", true);}
	|	"nonunique"	{ isUnique = false; modifiersUsed.put("unique", true); modifiersUsed.put("nonunique", true);}
	)
	;
	

//////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

/* #################################################
   ###                  LEXER                    ###
   #################################################
*/

///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////////////////

class ParameterLabelLexer extends Lexer;

options {
	k = 7;
	charVocabulary = '\0' .. '\u00FF';
}

// Definition of newlines
NL	
	:	(	'\r' '\n' 
		|	'\n' '\r'  //Improbable
		|	'\r'
		|	'\n'
		)
	{ $setType(Token.SKIP); newline();}
	;


// White spaces
WS
	:	(	' '
		|	'\t'   
		)
	;

QUESTION_MARK
	:	'?'
	;
	
LNOT
	:	'!'
	;
		
DOUBLE_QUOTE
	:	'\"'
	;

SINGLE_QUOTE	
	:	'\''
	;

LPAREN
	:	'('
	;

RPAREN
	:	')'
	;

LSQUARE
	:	'['
	;

RSQUARE
	:	']'
	;

LCURLY
	:	'{'
	;

DOUBLE_LCURLY
	:	"{{"
	;

RCURLY
	:	'}'
	;
	
DOUBLE_RCURLY
	:	"}}"
	;
	
LOGICAL_AND
	:	'&'
	;

DOUBLE_LOGICAL_AND
	:	"&&"
	;

PIPE
	:	'|'
	;

DOUBLE_PIPE
	:	"||"
	;

POINT
	:	'.'
	;

POINT_POINT
	:	".."
	;

COMMA
	:	','
	;

SEMI
	:	';'
	;

SHARP
	:	'#'
	;

TILDE
	:	'~'
	;

COLON
	:	':'
	;

DOUBLE_COLON
	:	"::"
	;

RULE_ASSIGN
	:	"::="
	;

ARROW
	:	"->"
	;

STAR
	:	'*'
	;

DIV
	:	'/'
	;

PLUS
	:	'+'
	;

MINUS
	:	'-'
	;

EQ
	:	"="
	;


ISEQ
	:	"=="
	;

ISNE
	:	"!="
	;

LT
	:	'<'
	;

DOUBLE_LT
	:	"<<"
	;

LE
	:	"<="
	;

GT
	:	'>'
	;

DOUBLE_GT
	:	">>"
	;

GE
	:	">="
	;


INCR
	:	"++"
	;

DECR
	:	"--"
	;

CALLOPERATION
	:	":="
	;

protected
DIGIT
	:	'0'..'9'
	;

protected
ALPHA
	:	'a'..'z' 
	|	'A'..'Z'
	;

protected
UNDERSCORE
	:	'_'
	;

protected
INTEGER
	:	(DIGIT)+
	;

protected
REAL
	:	INTEGER '.' INTEGER
	;

REAL_OR_INT
	:	(INTEGER '.' ~('.')) => REAL 	{$setType(REAL);}
	|	INTEGER			{$setType(INTEGER);}
	;

IDENTIFIER
	:	(ALPHA|UNDERSCORE)(ALPHA|DIGIT|UNDERSCORE)*
	;
	
	
